#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<unistd.h>
#include<cstring>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>

using namespace std;
const int basesize=1024;
const int gargvnum=64;
const int genvnum=64;
//全局变量
int lastcode=0;
//全局的命令行参数表
char *gargv[gargvnum];
int gargc=0;
//全局的当前shell工作路径,父子进程都能用
char pwd[basesize];
char pwdenv[basesize];
//全局环境变量
char* genv[genvnum];

string GetUser()
{
    string name=getenv("USER");
    return name.empty()?"None":name;
}
string GetHostName()
{
    string hostname=getenv("HOSTNAME");
    return hostname.empty()?"None":hostname;
}
string GetPwd()
{
    //使用cwd直接系统调用获取pwd更准确
    //直接将获取到的PWD写入pwd变量中
    if(getcwd(pwd,sizeof(pwd))==nullptr)
    {
        return "None";
    }
    //得到要写入env的字符串pwdenv 
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);     //PWD==xxx;
    return pwd;
    //上述不仅得到了准确的pwd,还更新了环境变量列表


    //pwd更新具有延后性,不能准确获取当前工作路径
    //string pwd=getenv("PWD");
    //return pwd.empty()?"None":pwd;
}

string GetLastDir()
{
    string curr=GetPwd();
    if(curr=="/"||curr=="None")
    {
        return curr;
    }
    size_t pos=curr.rfind("/"); //反向查找最后一个/
    if(pos==string::npos) return curr; //没找到 /
    return curr.substr(pos+1);
}

string MakeCommandLine()
{

    char Command_Line[basesize];
    //[shenyu@bite-alicloud myshell]$ 
    snprintf(Command_Line,basesize,"[%s@%s %s]$ ",
            GetUser().c_str(),GetHostName().c_str(),
            GetLastDir().c_str());
    return Command_Line;
}

void PrintfCommandLine()
{
    printf("%s",MakeCommandLine().c_str());
    fflush(stdout);   //刷新缓冲区
}

bool GetCommandLine(char *Command_buffer,size_t size)
{
    char* result=fgets(Command_buffer,size,stdin);
    if(result==nullptr)
    {
        return false;
    }
    //fgets会读取末尾的回车,处理一下回车键
    Command_buffer[strlen(Command_buffer)-1]=0;
    //只读取到一个回车的情况
    if(strlen(Command_buffer)==0)
    {
        return false;
    }
    return true;
}

//分析获取到的指令切割存到命令行参数表中
void ParseCommand(char Command_buffer[],size_t len)
{
    //无实际意义,只是为了避免警告
    (void)len;
    //初始化命令行参数列表
    memset(gargv,0,sizeof(gargv));
    gargc=0;

    const char *stp=" "; //分隔符
    gargv[gargc++]=strtok(Command_buffer,stp);
    while ((bool)(gargv[gargc++]=strtok(nullptr,stp)));
    gargc--;
}

//shell中
//有些程序要由子进程执行
//有些程序则必须由父进程执行,内建指令

void Addenv(const char* item)
{
    int i=0;
    while(genv[i])
    {
        i++;
    }
    genv[i]=(char *)malloc(strlen(item)+1);
    strncpy(genv[i],item,strlen(item)+1);
    genv[++i]=nullptr;
}

//执行命令
bool ExecuteCommand()
{
    pid_t id=fork();
    if(id<0)
    {
        return false;
    }
    else if(id==0)
    {
        execvpe(gargv[0],gargv,genv);
        exit(1);
    }
    int status=0;
    pid_t rid=waitpid(id,&status,0);
    if(rid>0)
    {
        if(WIFEXITED(status))
        {
            lastcode=WIFEXITED(status);
        }
        else 
        {
            lastcode=100;
        }
        return true;
    }
    else 
    {
        return false;
    }
}

//四大内建命令
//cd export env echo

bool CheckAndExecBuiltCommand()
{
    //cd ..   cd- ...
    if(strcmp(gargv[0],"cd")==0)
    {
        if(gargc==2)
        {
            chdir(gargv[1]);
            lastcode=0;
        }
        else 
        {
            lastcode=1;
        }
        return true;
    }
    else if(strcmp(gargv[0],"export")==0)
    {
        if(gargc==2)
        {
            Addenv(gargv[1]);
            lastcode=0;
        }
        else 
        {
            lastcode=2;
        }
        return true;
    }
    else if(strcmp(gargv[0],"env")==0)
    {
        for(int i=0;genv[i];i++)
        {
            printf("%s\n",genv[i]);
        }
        lastcode=0;
        return true;
    }
    else if(strcmp(gargv[0],"echo")==0)
    {
        if(gargc==2)
        {
            // echo $?
            // echo $PATH
            // echo hello
            if(gargv[1][0]=='$')
            {
                 if(gargv[1][1]=='?')
                {
                    printf("%d\n",lastcode);
                    lastcode=0;
                }
            else 
            {
                printf("%s",gargv[1]);    
            }
            }
        else 
        {
            lastcode=3;
        }
        return true;
        }
    }
    return false;
}

//真正的shell是从系统配置中获取的
//从bash中继承环境变量
void Initenviron()
{
    extern char** environ;
    //使用深拷贝
    int i=0;
    while(environ[i])
    {
        genv[i]=(char *)malloc(strlen(environ[i]+1));
        strncpy(genv[i],environ[i],strlen(environ[i])+1);
        i++;
    }
    genv[i]=nullptr;
    //浅拷贝,独立性较差
    //for(int i=0;environ[i];i++)
    //{
        //genv[i]=environ[i];
    //}
}



int main()
{
    //初始化环境变量
    Initenviron();
    //缓冲区
    char Command_Buffer[basesize];
    while(true)
    {
        //命令行提示符
        PrintfCommandLine();
        //获取指令
        //没有获取到指令就跳过当前循环
        if(!GetCommandLine(Command_Buffer,basesize))
        {
            continue;
        }
        //分析指令
         ParseCommand(Command_Buffer,basesize);

        if(CheckAndExecBuiltCommand())  //检测内建指令
        {
            continue;
        }
        //执行指令
        ExecuteCommand();
    }
    return 0;
}
